【ARM Cortex-M 开发实战指南(基础篇)】第8章 PWM输出

开发环境:
MDK:Keil 5.30
STM32CubeMX:V6.4.0
MCU:STM32F103ZET6

8.1 PWM输出的工作原理

脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。
STM32 的定时器除了 TIM6 和 7(基本定时器)。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。

每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,,将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)。

在上一节,讲解了定时器的相关寄存器即基本原理,本节将不再赘述。下面谈谈如何使用定时器的寄存器进行PWM输出的。若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。

估计很多初学者看了上面的一段话都很蒙圈,没关系,下面以向上计数模式为例进行讲解。

XQJVl6.png

在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值)。当CNT小于CCRx时,TIMx_CHx通道输出低电平;当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。因此得到PWM的一个周期如下:

1.定时器从0开始向上计数;
2.当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平;
3.t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平;
4.当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数…循环此过程。
至此一个PWM周期完成。针对PWM重点关注两个寄存器,TIMx_ARR寄存器确定PWM频率,TIMx_CCRx寄存器确定占空比。

上文提到了PWM的输出模式,下面讲解PWM的工作模式:

  • PWM模式1(向上计数) :计数器从0计数加到自动重装载值(TIMx_ARR),然后重新从0开始计数,并且产生一个计数器溢出事。
  • PWM模式2(向下计数) :计数器从自动重装载值(TIMx_ARR)减到0,然后重新从重装载值(TIMx_ARR)开始递减,并且产生一个计数器溢出事件。

这里我们仅利用 TIM3产生多路 PWM 输出。如果要产生多路输出,大家可以根据我们的代码稍作修改即可。具体不同定时器对应引脚在对应芯片数据手册的引脚说明(pin description) 中查看。

[ps] 本文以F1系列为例进行讲解,ST不同系列其定时器个数不同

STM32F1系列共有8个定时器:
高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。

8.2 PWM输出的寄存器描述

同样,我们首先通过对 PWM 相关的寄存器进行讲解,大家了解了定时器 TIM3 的 PWM原理之后,我们再讲解怎么使用库函数产生 PWM 输出。

要使 STM32 的通用定时器 TIMx 产生 PWM 输出,除了上一章介绍的寄存器外,我们还会用到 3 个寄存器,来控制 PWM 的。这三个寄存器分别是:捕获 /比较模式寄存器( TIMx_CCMR1/2)、捕获/比较使能寄存器( TIMx_CCER)、捕获/比较寄存器( TIMx_CCR1~4)。接下来我们简单介绍一下这三个寄存器。

首先是捕获/比较模式寄存器( TIMx_CCMR1/2),该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。 TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如下图。

XQJ8pt.md.png

该寄存器的有些位在不同模式下,功能不一样,所以在上图中,我们把寄存器分了 2层,上面一层对应输出而下面的则对应输入。关于该寄存器的详细说明,请参考《 STM32 参考手册》。这里我们需要说明的是模式设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种PWM 模式的区别就是输出电平的极性相反。
接下来,我们介绍捕获/比较使能寄存器( TIMx_CCER),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如下图。

XQJG1P.md.png

该寄存器比较简单, 我们这里只用到了 CC2E 位,该位是输入/捕获 2 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。

最后,我们介绍一下捕获/比较寄存器( TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍,该寄存器的各位描述如下图。

XQJYX8.md.png

在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。

假如我们要利用 TIM3 的 CH2 输出 PWM 来控制 DS0 的亮度,但是 TIM3_CH2 默认是接在 PA7上面的,这就可以通过重映射功能,把 TIM3_CH2映射到 PB5 上。

XQJwkj.md.png

STM32 的重映射控制是由复用重映射和调试 IO 配置寄存器( AFIO_MAPR)控制的,该寄存器的各位描述如上图。

我们这里用到的是 TIM3 的重映射,从上图可以看出, TIM3_REMAP 是由[11:10]这 2 个位控制的。 TIM3_REMAP[1:0]重映射控制表如下表。

XQJBhn.md.png

【注】重映像只适用于 64、 100 和 144 脚的封装。

默认条件下, TIM3_REMAP[1:0]为 00,是没有重映射的,所以 TIM3_CH1~TIM3_CH4 分别是接在 PA6、 PA7、 PB0 和 PB1 上的,而我们想让 TIM3_CH2 映射到 PB5 上, 则需要设置TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时 TIM3_CH1 也被映射到 PB4 上了。

下面总结下PWM的工作过程:

XQJrpq.md.png

1.CCR1寄存器:捕获/比较值寄存器:设置比较值;
计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
OC1REF=0 无效电平
OC1REF=1 无效电平

2.TIMx_CCMR1寄存器:OC1M[2:0]位:用于设置PWM模式
110:PWM模式1
111:PWM模式2

3.CCER寄存器:CC1P位:输入/捕获1输出极性。
0:高电平为有效电平
1:低电平为有效电平

4.CCER寄存器:CC1E位:输入/捕获1输出使能。
0:关闭使能
1:打开使能

5.输出电平信号
TIM定时器的四路通道TIMx_CHx输出PWM

XQJycV.png

8.3 PWM输出实现-标准库

8.3.1 PWM代码分析

本章要实现通过TIM3实现四路方波的输出,以TIM3_CH1 输出 PWM 为例进行讲解。下面我们介绍通过库函数来配置该功能的步骤。

首先要提到的是,PWM 相关的函数设置在库函数文件 stm32f10x_tim.h和 stm32f10x_tim.c文件中。

1) 开启 TIM3 时钟以及GPIO的时钟,配置 PA6为复用输出。

要使用 TIM3,我们必须先开启 TIM3 的时钟,这点相信大家看了这么多代码,应该明白了。库函数使能 TIM3 及PA6时钟的方法是:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器 3 时钟

库函数设置 AFIO 时钟的方法是:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能

设置 PA6为复用功能输出的方法在前面的几个实验都有类似的讲解,相信大家很明白,这里简单列出 GPIO 初始化的一行代码即可:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

2) 初始化 TIM3,设置 TIM3 的 ARR 和 PSC。

在开启了 TIM3 的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。当 PWM 周期太慢(低于 50Hz)的时候,我们就会明显感觉到闪烁了。因此, PWM 周期在这里不宜设置的太小。 这在库函数是通过 TIM_TimeBaseInit 函数实现的,在上一节定时器中断章节我们已经有讲解,这里就不详细讲解,调用的格式为:

TIM_TimeBaseStructure.TIM_Period = Arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler =Psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的

3) 设置 TIM3_CH1的 PWM 模式,使能 TIM3 的 CH1 输出。

接下来,我们要设置 TIM3_CH1为 PWM 模式(默认是冻结的),在库函数中, PWM 通道设置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样, 这里我们使用的是通道1,我们直接来看看结构体 TIM_OCInitTypeDef的定义:

typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState; */
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;

这里我们讲解一下与我们要求相关的几个成员变量:

参数 TIM_OCMode 设置模式是 PWM 还是输出比较,这里我们是 PWM 模式。
参数 TIM_OutputState 用来设置比较输出使能,也就是使能 PWM 输出到端口。
参数 TIM_OCPolarity 用来设置极性是高还是低。
其他的参数 TIM_OutputNState, TIM_OCNPolarity, TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的。

要实现我们上面提到的场景,方法是:

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;       //配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //设置跳变值,当计数器计数到这个值时,电平发生跳变
TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //当定时器计数值小于CCR1_Val时为高电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  
TIM_OC1Init(TIM3, &TIM_OCInitStructure);     //使能通道1
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

4) 使能 TIM3。

TIM_ARRPreloadConfig(TIM3, ENABLE);          // 使能TIM3重载寄存器

在完成以上设置了之后,我们需要使能 TIM3。使能 TIM3 的方法前面已经讲解过:

TIM_Cmd(TIM3, ENABLE); //使能 TIM3

最后看下主函数代码:

/**
  * @brief     主函数
  * @param     None
  * @retval    int
  */
int main(void)
{
    /*SysTick Init*/
    SysTick_Init();

    /* TIM3 PWM波输出初始化,并使能TIM3 PWM输出 */
    TIM3_PWM_Init(999,0);
    while (1)
    {
    }
}

是不是很简单,这里进行了PWM初始化,并对TIM3进行使能。

/**
  * @brief  TIM3初始化
  * @param  Arr 定时周期
            Psc 预分频
  * @retval None
  */
/*****************************************************
          TIM3 输出PWM信号初始化,只要调用这个函数
          TIM3的四个通道就会有PWM信号输出
*****************************************************/
void TIM3_PWM_Init(uint16_t Arr,uint16_t Psc)
{
    TIM3_GPIO_Config();
    TIM3_Mode_Config(Arr,Psc);
}

TIM3_GPIO_Config()函数没什么说的,就是配置相应的GPIO,最核心的就是TIM3_Mode_Config()函数,其代码如下:

/**
  * @brief  配置TIM3输出的PWM信号的模式,如周期、极性、占空比
  * @param  None
  * @retval None
  */
/*******************************************************
 * TIMxCLK/CK_PSC --> TIMxCNT --> TIMx_ARR --> TIMxCNT 重新计数
 *                    TIMx_CCR(电平发生变化)
 * 信号周期=(TIMx_ARR +1 ) * 时钟周期
 * 占空比=TIMx_CCR/(TIMx_ARR +1)
 ******************************************************/
static void TIM3_Mode_Config(uint16_t Arr, uint16_t Psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    /* PWM信号电平跳变值 */
    uint16_t CCR1_Val = 500;
    uint16_t CCR2_Val = 375;
    uint16_t CCR3_Val = 250;
    uint16_t CCR4_Val = 125;

    /* ----------------------------------------------------------------------- 
    TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR+1)* 100% = 50%
    TIM3 Channel2 duty cycle = (TIM3_CCR2/ TIM3_ARR+1)* 100% = 37.5%
    TIM3 Channel3 duty cycle = (TIM3_CCR3/ TIM3_ARR+1)* 100% = 25%
    TIM3 Channel4 duty cycle = (TIM3_CCR4/ TIM3_ARR+1)* 100% = 12.5%
    ----------------------------------------------------------------------- */

    /* Time base configuration */
    TIM_TimeBaseStructure.TIM_Period = Arr;       //当定时器从0计数到999,即为1000次,为一个定时周期
    TIM_TimeBaseStructure.TIM_Prescaler = Psc;    //设置预分频:不预分频,即为72MHz
    //TIM_TimeBaseStructure.TIM_Period = 999;       //当定时器从0计数到999,即为1000次,为一个定时周期
    //TIM_TimeBaseStructure.TIM_Prescaler = 0;     //设置预分频:不预分频,即为72MHz

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;  //设置时钟分频系数:不分频(这里用不到)
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //配置为PWM模式1

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = CCR1_Val;   //设置跳变值,当计数器计数到这个值时,电平发生跳变
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //当定时器计数值小于CCR1_Val时为高电平
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);   //使能通道1
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

    /* PWM1 Mode configuration: Channel2 */
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = CCR2_Val;  //设置通道2的电平跳变值,输出另外一个占空比的PWM
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //使能通道2
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    /* PWM1 Mode configuration: Channel3 */
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = CCR3_Val;  //设置通道3的电平跳变值,输出另外一个占空比的PWM
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //使能通道3
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

    /* PWM1 Mode configuration: Channel4 */
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = CCR4_Val;  //设置通道4的电平跳变值,输出另外一个占空比的PWM
    TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //使能通道4
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM3, ENABLE);  // 使能TIM3重载寄存器ARR

    /* TIM3 enable counter */
    TIM_Cmd(TIM3, ENABLE);  //使能定时器3
}

8.3.2 PWM周期、占空比分析

根据前面的参数配置,我们可以算出PWM的输出周期:
PWM=1/(Tclk/(psc+1))*(arr+1)

这里我们 arr=999 psc=0 Tclk=72Mhz ,
PWM=1/(72Mhz/(1))*(999+1)=1/72ms

因此PWM的输出频率72KHz,周期是13.9us。

PWM的占空比为:

Dutycycle=(TIMx_CCR/ TIMx_ARR+1)* 100%

PWM自动重装值为999,四个通道的跳变值分别为500,375,250,125。因此,TIM3的四个通道的占空比分别为50%,37.5%,25%,12.5%。

8.4 PWM输出实现-HAL库

8.4.2 STM32Cube生成工程

本文介绍在STM32CubeMX进行定时器的配置,这里我们仅利用 TIM3的 4路通道输出,方便我们比较波形。具体不同定时器对应引脚在对应芯片数据手册的引脚说明(pin description) 中查看。

1.设置RCC

设置高速外部时钟HSE,选择外部时钟源。

XQJTc6.md.png

2.时钟配置

笔者的板子使用的外部晶振为8MHz,选择外部时钟HSE 8MHz ,PLL锁相环9倍频后为72MHz,系统时钟来源选择为PLL,设置APB1分频器为 /2,这时候定时器的时钟频率为72Mhz。本文笔者使用的定时器是TIM3,TIM3挂在APB1上,不同的定时器挂在不同总线上的。

XQJbnO.md.png

3.Times配置
选择TIM,使能TIM3,指定时钟源。

XQYiDS.md.png

【注】TIM3的时钟源有两个选项

选项1 :Internal Clock 内部时钟
选项2 : ETR2 外部触发输入(ETR)(仅适用TIM2,3,4)

本文要使用TIM3的四个通道,因此需要将其使能。每个通道有很多模式,这里选择PWM输出。当对应的通道打开后,对应的GPIO也会被使能。

XQYeCn.md.png

【注】如果使能通道前通道中GPIO使用过,STM32CubeMX会自动将GPIO配置为重映射的GPIO。举个例子,当PB0被占用了,那么四个GPIO会重映射到PC6-PC9。

PWM参数配置如下:

Counter setting

Prtscaler (定时器分频系数) : 0
Counter Mode(计数模式) :Up(向上计数模式)
Counter Period(自动重装载值) : 999
CKD(时钟分频因子) : No Division 不分频
选项: 可以选择二分频和四分频
auto-reload-preload(自动重装载) : Enable 使能

TRGO Output (TRGO) Parameters
Master/Slave Mode(MSM bit):Disable
TRGO:定时器的触发信号输出 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换,)

PWM Generation Channel (四个CH)
Mode(定时模式):PWM mode 1
Pulse(计数比较值):四个通道分别为500,375,250,125
CH Polarity(输出极性):High

XQYMuT.md.png

根据前面的参数配置,我们可以算出PWM的输出周期,这里我们 arr=999 psc=0 Tclk=72Mhz ,

PWM = 1/(72Mhz/(1)) * (999+1) = 1/72ms

本文选择的是PWM模式1,在向上计数时,一旦TIMx_CNT < TIMx_CCR1(计数比较值)。时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。输出比较极性的指的是你在比较匹配之后输出口输出的极性,也就是设置比较输出的有效电平。你可以设置为高电平有效或者低电平有效。如果设置为高电平有效,那么当定时器比较匹配之后,输出口输出高电平,否则就反一下。

如果是PWM模式1,且向上计数,如果极性设置为低,那么 TIMx_CNT < TIMx_CCR1 时,输出低电平,更简单就是占空比为1 -TIMx_CCR1/(ARR+1). 如果极性为高,占空比就是TIMx_CCR1/(ARR+1)。

好了,到这里,配置就完成了,生成工程就行了。

8.4.2 PWM输出的具体代码分析

我们先看看主函数,其代码如下:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
  /*使能定时3*/
  HAL_TIM_Base_Start_IT(&htim3);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
  //HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_ALL);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

在主循环前面,需要对TIM3进行初始化配置:

HAL_TIM_Base_Start_IT(&htim3);

然后再开启四路通道的PWM:

HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);

如果全部开启,可使用以下代码:

HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_ALL);

PWM输出最重要就是MX_TIM3_Init()函数,这个函数包含了TIM3的PWM配置。

8.5 PWM输出的实验现象

在前面我们输出了TIM3 的通道 1(PA6)、2(PA7)、3(PB0)、4(PB1)不同占空比的 PWM 信号。接下来就看看PWM的输出,PWM 信号可以通过示波器看到,但是很多朋友可能没有逻辑分析仪,我们在这里先采用软件仿真的方式来验证我们的程序。

这里不需要将代码下载到板子中,因此需要配置成软件仿真,首先需要设置一下我们的开发环境,按照如下步骤所示。

1)点击 Target Options 选项图标,选中 Debug 选项卡,选中 Use Simulator 选项,按图中所示进行设置,然后点击“OK”按钮,见下图。

xcfhXd.md.png

2)点击 Start/Stop Debug Session 选项图标,点击System Analysis Windows的下拉选项的 Logic Analysis,弹出窗口后点击Setup…选项卡,在弹出的 Setup Logic Analysis 串口中点击 New(Insert)按钮,然后在文本栏里面分别输入:PORTA.6 、PORTA.7、PORTB.0、PORTB.1,记住,是 New 一个就输入一个信号的 IO,输完之后需要再 New。对应的相关设置见下图。

XQY8UJ.png

需要注意的是Display Range的设置,默认的设置是看不到波形的。

3)设置完信号源之后,点击 RUN 按钮,仿真信号即出来了,当信号出来之后,可点击STOP 按钮,让信号不再变化,方便观察。其中 In、Out、All 这三个按钮可以调节显示信号的疏密程度,见下图。

XQYt81.md.png

其中 Cursor 选项可以帮助我们测量信号的时间差,Amplitute 则可以帮助我们测量信号的幅值。

XQYUv6.md.png

通过使用Cursor可以得到自动重装载和跳变值个数,还能看到信号的时间差。从上图可以测量,一个周期的时间为13.89us,自动重装载值为1000,还能得到各个通道的高电平时间和跳变值个数,从而得到占空比,分别是50%,37.5%,25%,12.5%,和计算值是相符的,毕竟这是软件仿真,没啥好说的。

有条件的朋友还可以用逻辑分析仪器查看波形,下面笔者就是用逻辑分析仪查看波形。

首先笔者使用的逻辑分析仪是Kingst LA5016,当然啦,其他的也可以,关于逻辑分析仪的相关使用笔者这里就不介绍了,可以查看官方资料。

首先将通道 1(PA6)、2(PA7)、3(PB0)、4(PB1)分别接到逻辑分析仪的CH0 – CH3,然后下载程序到板子中,打开Kingst VIS,然后进行采样。

XQYrUH.md.png

我们就可以看到不同通道的实际周期,占空比等信息。

XQYc8I.md.png

从上图可以看到,实际测量的频率和占空比和理论是相符的。


欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎


资源获取方式

1.关注公众号[嵌入式实验楼]
2.在公众号回复关键词[Cortex-M]获取资料提取码

Related posts

Leave a Comment